package clw.toy;

import java.util.Objects;

/**
 * @author chengliwei
 */
public class AstPrinter implements Expr.Visitor<String> {

    public String print(Expr expr) {
        return expr.accept(this);
    }

    @Override
    public String visitBinary(Expr.Binary binary) {
        return parenthesize(binary.operator.lexeme, binary.left, binary.right);
    }

    @Override
    public String visitGrouping(Expr.Grouping grouping) {
        return parenthesize("group", grouping.expression);
    }

    @Override
    public String visitLiteral(Expr.Literal literal) {
        if (Objects.isNull(literal)) {
            return "nil";
        }
        return literal.value.toString();
    }

    @Override
    public String visitUnary(Expr.Unary unary) {
        return parenthesize(unary.operator.lexeme, unary.right);
    }

    @Override
    public String visitVariableExpr(Expr.Variable variable) {
        return "var " + variable.name.lexeme;
    }

    @Override
    public String visitAssignExpr(Expr.Assign assign) {
        return assign.name.lexeme + " = " + assign.value.accept(this);
    }

    @Override
    public String visitThisExpr(Expr.This thisExpr) {
        return null;
    }

    @Override
    public String visitLogicalExpr(Expr.Logical logical) {
        return null;
    }

    @Override
    public String visitCallExpr(Expr.Call call) {
        return call.callee.accept(this);
    }

    @Override
    public String visitGetExpr(Expr.Get get) {
        return null;
    }

    @Override
    public String visitSetExpr(Expr.Set set) {
        return null;
    }

    @Override
    public String visitSuperExpr(Expr.Super superExpr) {
        return null;
    }

    @Override
    public String visitSelfIncrementOrDecrement(Expr.SelfIncrementOrDecrement expr) {
        return expr.left.accept(this) + expr.operator.lexeme;
    }

    private String parenthesize(String name, Expr... exprs) {
        StringBuilder builder = new StringBuilder();
        builder.append("(").append(name);
        for (Expr expr : exprs) {
            builder.append(" ");
            builder.append(expr.accept(this));
        }
        builder.append(")");
        return builder.toString();
    }

    public static void main(String[] args) {
        Expr expression = new Expr.Binary(
                new Expr.Unary(
                        new Token(TokenType.MINUS, "-", null, 1),
                        new Expr.SelfIncrementOrDecrement(new Expr.Literal(123), new Token(TokenType.INC, "++", null, 1))
                ),
                new Token(TokenType.STAR, "*", null, 1),
                new Expr.Grouping(
                        new Expr.Literal(45.67)
                )
        );

        System.out.println(new AstPrinter().print(expression));
    }
}
